package com.yuanchu.marketing.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yuanchu.common.exception.YuanChuException;
import com.yuanchu.common.utils.DateFormatUtils;
import com.yuanchu.common.utils.PageResult;
import com.yuanchu.common.utils.R;
import com.yuanchu.marketing.dao.YoshopCouponDao;
import com.yuanchu.marketing.dao.YoshopUserCouponDao;
import com.yuanchu.marketing.entity.YoshopCouponEntity;
import com.yuanchu.marketing.entity.YoshopUserCouponEntity;
import com.yuanchu.marketing.service.YoshopCouponService;
import com.yuanchu.marketing.service.YoshopUserCouponService;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.Instant;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Slf4j
@Service("yoshopUserCouponService")
public class YoshopUserCouponServiceImpl extends ServiceImpl<YoshopUserCouponDao, YoshopUserCouponEntity> implements YoshopUserCouponService {

    @Resource
    private YoshopCouponService couponService;
    @Resource
    private YoshopCouponDao couponDao;
    @Resource
    private RedissonClient redissonClient;
    @Resource
    private YoshopUserCouponService userCouponService;

    @Override
    public List<YoshopUserCouponEntity> queryUserCouponById(Integer couponId, Integer userId) {
        long nowTime = Instant.now().toEpochMilli() / 1000;
        LambdaQueryWrapper<YoshopUserCouponEntity> lqw = new LambdaQueryWrapper<>();
        if (couponId != null && couponId > 0) {
            lqw.eq(YoshopUserCouponEntity::getCouponId, couponId);
        }
        lqw.eq(YoshopUserCouponEntity::getUserId, userId);
        // todo是否过期
        lqw.eq(YoshopUserCouponEntity::getIsExpire, 0);
        // 是否已使用
        lqw.eq(YoshopUserCouponEntity::getIsUse, 0);
        // 开始时间小于当前时间
        lqw.lt(YoshopUserCouponEntity::getStartTime, nowTime);
        // 结束时间大于当前时间
        lqw.gt(YoshopUserCouponEntity::getEndTime, nowTime);
        List<YoshopUserCouponEntity> list = this.list(lqw);
        list.forEach(YoshopUserCouponServiceImpl::setIsExpireCoupon);
        return list;
        /* return list.stream().filter(coupon -> coupon.getIsExpire() == 0).collect(Collectors.toList()); */
    }

    private static void setIsExpireCoupon(YoshopUserCouponEntity coupon) {
        // 获取当前时间戳
        long nowTimestamp = Instant.now().toEpochMilli() / 1000;
        // 判断当前时间是否在优惠券有效期内
        if (coupon.getIsUse() == 0 && nowTimestamp >= Long.parseLong(coupon.getStartTime()) && nowTimestamp <= Long.parseLong(coupon.getEndTime())) {
            Map<String, Object> map = new HashMap<>();
            map.put("text", "正常");
            map.put("value", 1);
            coupon.setState(map);
        } else if (coupon.getIsUse() == 1) {
            Map<String, Object> map = new HashMap<>();
            map.put("text", "已使用");
            map.put("value", 0);
            coupon.setState(map);
        } else {
            Map<String, Object> map = new HashMap<>();
            map.put("text", "已过期");
            map.put("value", 0);
            coupon.setIsExpire(1);
            coupon.setState(map);
        }
        formatDateTime(coupon);
    }

    @Override
    public PageResult<YoshopUserCouponEntity> queryUserCouponList(String dataType, Integer page, Integer userId) {
        LambdaQueryWrapper<YoshopUserCouponEntity> lqw = new LambdaQueryWrapper<>();
        if (page == null || page == 0) {
            page = 1;
        }
        lqw.eq(YoshopUserCouponEntity::getUserId, userId);
        lqw.orderByAsc(YoshopUserCouponEntity::getEndTime);
        // // 是否未使用
        // if ("isUnused".equals(dataType)){
        //     lqw.eq( YoshopUserCouponEntity::getIsUse, 0);
        //     lqw.eq( YoshopUserCouponEntity::getIsExpire, 0);
        // }
        // // 是否已使用
        // lqw.eq("isUse".equals(dataType), YoshopUserCouponEntity::getIsUse, 1);
        // //  是否过期
        // lqw.eq("isExpire".equals(dataType), YoshopUserCouponEntity::getIsExpire, 1);
        Page<YoshopUserCouponEntity> page1 = new Page<>(page, 15);
        Page<YoshopUserCouponEntity> pageResult = this.page(page1, lqw);
        List<YoshopUserCouponEntity> records = pageResult.getRecords();
        records.forEach(YoshopUserCouponServiceImpl::setIsExpireCoupon);

        // 是否过期
        if ("isExpire".equals(dataType)) {
            // 查询过期的优惠券
            List<YoshopUserCouponEntity> expireCoupons = records.stream().filter(coupon -> coupon.getIsExpire() == 1).collect(Collectors.toList());
            return getPageResult(page, expireCoupons);
        }
        // 是否已使用
        if ("isUse".equals(dataType)) {
            // 查询已使用的优惠券
            List<YoshopUserCouponEntity> useCoupons = records.stream().filter(coupon -> coupon.getIsUse() == 1).collect(Collectors.toList());
            return getPageResult(page, useCoupons);
        }
        // 是否未使用
        if ("isUnused".equals(dataType)) {
            // 查询未使用的优惠券
            List<YoshopUserCouponEntity> unusedCoupons = records.stream()
                    .filter(coupon -> coupon.getIsExpire() == 0)
                    .filter(coupon -> coupon.getIsUse() == 0)
                    .collect(Collectors.toList());
            return getPageResult(page, unusedCoupons);
        }
        return getPageResult(page, records);
    }

    @Override
    @Transactional
    public R receiveCoupon(Integer couponId, Integer userId, int storeId) {
        // 1.查询优惠券
        YoshopCouponEntity coupon = couponService.getById(couponId);
        if (coupon == null) {
            return R.error("优惠券不存在");
        }
        // 优惠券类型
        Integer expireType = coupon.getExpireType();
        if (expireType == 10) {
            // 10领取后生效
        }
        // 固定时间
        // 2.判断秒杀是否开始
        if (new BigDecimal(coupon.getStartTime()).compareTo(new BigDecimal(Instant.now().toEpochMilli() / 1000)) > 0) {
            // 尚未开始
            return R.error("秒杀尚未开始！");
        }
        // 3.判断秒杀是否已经结束
        if (new BigDecimal(coupon.getEndTime()).compareTo(new BigDecimal(Instant.now().toEpochMilli() / 1000)) < 0) {
            // 尚未开始
            return R.error("秒杀已经结束！");
        }
        // 4.判断库存是否充足
        if (coupon.getTotalNum() < 1 && (coupon.getTotalNum() != -1)) {
            // -1为不限制
            return R.error("库存不足"); // 库存不足
        }
        //创建分布式锁对象
        RLock lock = redissonClient.getLock("lock:coupon:" + userId);
        //获取锁对象
        boolean isLock = lock.tryLock();
        //加锁失败
        if (!isLock) {
            return R.error("不允许重复下单");
        }
        try {
            //获取代理对象(事务)
            return userCouponService.createCouponOrder(coupon, userId, storeId);
        } finally {
            //释放锁
            lock.unlock();
        }
    }

    @Transactional
    public R createCouponOrder( YoshopCouponEntity coupon, Integer userId, int storeId) {
        // 5.一人一单逻辑
        int count = this.query().eq("user_id", userId).eq("coupon_id", coupon.getCouponId()).count();
        // 5.2.判断是否存在
        if (count > 0) {
            log.error("用户["+userId+"]已经购买过一次！");
            return R.error("用户已经购买过一次！");
        }
        // 5，扣减库存
        Integer integer = couponDao.updateCouponNum(coupon.getCouponId(), storeId);
        if (integer == 0) {
            log.error("库存减少失败");
            throw new YuanChuException("库存减少失败");
        }
        // 6.创建订单
        YoshopUserCouponEntity userCoupon = new YoshopUserCouponEntity();
        BeanUtils.copyProperties(coupon, userCoupon);
        if (coupon.getExpireType() == 10) {
            Integer expireDay = coupon.getExpireDay();
            // 计算优惠券的开始时间毫秒值（领取后立即生效）
            long startMillis = Instant.now().toEpochMilli();
            // 计算优惠券的结束时间毫秒值（领取后七天内有效）
            long endMillis = startMillis + expireDay * 24 * 60 * 60 * 1000; // 加上七天的毫秒数
            userCoupon.setStartTime(String.valueOf(startMillis / 1000));
            userCoupon.setEndTime(String.valueOf(endMillis / 1000));
        }
        userCoupon.setUserId(userId);
        userCoupon.setCreateTime(String.valueOf(Instant.now().toEpochMilli() / 1000));
        userCoupon.setUpdateTime(String.valueOf(Instant.now().toEpochMilli() / 1000));
        boolean save = this.save(userCoupon);
        if (!save) {
            log.error("保存订单失败");
            throw new YuanChuException("保存订单失败");
        }
        return R.ok("领取成功");
    }

    @Override
    @Transactional
    public boolean updateUserCouponStatus(Integer userId, Integer couponId, Integer storeId) {
        return this.update(new UpdateWrapper<YoshopUserCouponEntity>().eq("user_id", userId)
                .eq("coupon_id", couponId).eq("store_id", storeId)
                .set("is_use", 1)
                .set("update_time", String.valueOf(Instant.now().toEpochMilli() / 1000)));
    }


    private static void formatDateTime(YoshopUserCouponEntity coupon) {
        coupon.setCreateTime(DateFormatUtils.formatDatetime(coupon.getCreateTime()));
        coupon.setUpdateTime(DateFormatUtils.formatDatetime(coupon.getUpdateTime()));
        coupon.setStartTime(DateFormatUtils.formatDatetimeFormat(coupon.getStartTime()));
        coupon.setEndTime(DateFormatUtils.formatDatetimeFormat(coupon.getEndTime()));
    }

    private static PageResult<YoshopUserCouponEntity> getPageResult(Integer page, List<YoshopUserCouponEntity> coupons) {
        PageResult<YoshopUserCouponEntity> result = new PageResult<>();
        result.setTotal(Math.toIntExact(coupons.size()));
        result.setCurrentPage(page);
        result.setData(coupons);
        result.setPerPage(15);
        long total = coupons.size();
        // 计算总页数，向下取整避免超出实际页数（这里已假设pageInfo.getTotal()返回的是long类型）
        int totalPages = (int) Math.ceil((double) total / 15);
        result.setLastPage(totalPages);
        return result;
    }
}